// Every Picture In The Universe 0.1
// Started by Vander 'imerso' Nunes on July 2012
// To contact me: imerso@imersiva.com
//
// Given enough time, generates every possible
// image for a given resolution -- which includes all your
// pictures in the past, present and future, plus
// you playing guitar together with David Gilmour
// on Rock in Rio, etc.
// OK, that may take a few billion years depending
// on the selected image resolution, but hey,
// one day that will happen -- hmm well, I guess.
//
// license: http://opensource.org/licenses/lgpl-2.1
//
// Although it seems like the program is just
// randomizing pixels, this is definitely not
// the case. A linear loop is too boring,
// so I'm trying to create alternative ways
// of permute color combinations in more
// interesting ways. Some may be buggy at
// first, but eventually this will combine
// using interesting, dynamic patterns
// until something meaninful happens.
//
// Hope you get the idea.
//
// most obvious enhancements:
//
// - a start dialog to select options
// - mono and grayscale support (for way faster permutations)
// - a time control to skip directly to a given time
// - pause the simulation
// - save both the 'time' and the generated image.

#include "sw_screen.h"


CScreen::CScreen()
{
	m_pDDraw = NULL;
	m_wWidth = m_wHeight = m_wDepth = 0;
	m_bFullScreen = false;
	m_hWnd = NULL;
	m_pFrameBuffer = NULL;
	m_pZBuffer = NULL;
}


CScreen::~CScreen()
{
	if (m_pFrameBuffer)
		delete [] m_pFrameBuffer;

	if (m_pZBuffer)
		delete [] m_pZBuffer;

	if (m_pDDraw)
	{
		if (m_bFullScreen)
		{
			m_pDDraw->RestoreDisplayMode();
			m_pDDraw->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL);
		}

		m_pDDraw->Release();
	}
}


bool CScreen::Init(WNDPROC pWndProc, WORD wWidth, WORD wHeight, WORD wDepth, bool bFullScreen, HWND hParent, WORD wWinWidth, WORD wWinHeight, char *szScrTitle)
{
	static char szClassName[] = "S3DE";
	WNDCLASS wndWc;
	HINSTANCE hInstance = GetModuleHandle(NULL);

	m_wWidth = wWidth;
	m_wHeight = wHeight;
	m_wDepth = wDepth;

	m_wWinWidth = wWinWidth;
	m_wWinHeight = wWinHeight;

	//
	// register window class
	//

	wndWc.style = 0;
	wndWc.lpfnWndProc = (WNDPROC) pWndProc;
	wndWc.cbClsExtra = 0;
	wndWc.cbWndExtra = 0;
	wndWc.hInstance = hInstance;
	wndWc.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
	wndWc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndWc.hbrBackground = NULL;
	wndWc.lpszMenuName = NULL;
	wndWc.lpszClassName = szClassName;

	RegisterClass(&wndWc);

	//
	// Create window
	//

	m_bFullScreen = bFullScreen;

	if (!bFullScreen)
	{
		//
		// Windowed
		//

		wWinWidth  += GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXBORDER);
		wWinHeight += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);

		m_hWnd = CreateWindow(szClassName, szScrTitle, WS_OVERLAPPEDWINDOW&(~WS_SIZEBOX&~WS_MAXIMIZEBOX),
		(GetSystemMetrics(SM_CXSCREEN)-wWinWidth)/2, (GetSystemMetrics(SM_CYSCREEN)-wWinHeight)/2,
		wWinWidth, wWinHeight, hParent, NULL, hInstance, NULL);

		if (!m_hWnd) return false;
	}
	else
	{
		//
		// Full Screen
		//

		m_hWnd = CreateWindow(szClassName, szScrTitle, WS_POPUP,
		0, 0,
		wWinWidth, wWinHeight, hParent, NULL, hInstance, NULL);

		if (!m_hWnd) return false;

		if (DirectDrawCreate(NULL, &m_pDDraw, NULL) != DD_OK) return false;
		if (m_pDDraw->SetCooperativeLevel(m_hWnd, DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN) != DD_OK) return false;
		if (m_pDDraw->SetDisplayMode(wWinWidth, wWinHeight, wDepth) != DD_OK) return false;
	}

	// Frame-buffer description
	// used to blit the frame-buffer to the screen.
	m_Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_Info.bmiHeader.biWidth = m_wWidth;
	m_Info.bmiHeader.biHeight = -m_wHeight;
	m_Info.bmiHeader.biPlanes = 1;
	m_Info.bmiHeader.biBitCount = 32;
	m_Info.bmiHeader.biCompression = BI_RGB;
	m_Info.bmiHeader.biSizeImage = 0;
	m_Info.bmiHeader.biXPelsPerMeter = 0;
	m_Info.bmiHeader.biYPelsPerMeter = 0;
	m_Info.bmiHeader.biClrUsed = 0;
	m_Info.bmiHeader.biClrImportant = 0;

	// create an offscreen dc to receive the pixel buffer
	HDC hDC = GetDC(m_hWnd);
	m_hBmOff = CreateBitmap(wWidth, wHeight, 1, 32, m_pFrameBuffer);
	m_hDcOff = CreateCompatibleDC(hDC);
	HBITMAP m_hOldBmOff = (HBITMAP)SelectObject(m_hDcOff, m_hBmOff);
	ReleaseDC(m_hWnd, hDC);

	// 32bit frame-buffer
	m_pFrameBuffer = new BYTE[m_wWidth * (m_wHeight+2) * sizeof(DWORD)];
	if (!m_pFrameBuffer) return false;

	// 32bit Z-Buffer
	m_pZBuffer = new BYTE[m_wWidth * m_wHeight * sizeof(DWORD)];
	if (!m_pZBuffer) return false;

	ShowWindow(m_hWnd, SW_SHOW);
	UpdateWindow(m_hWnd);

	return true;
}


void CScreen::Flip()
{
	HDC hDC = GetDC(m_hWnd);

	SetDIBitsToDevice(m_hDcOff,0,0,m_wWidth,m_wHeight,0,0,0,m_wHeight,(LPVOID)m_pFrameBuffer,&m_Info,DIB_RGB_COLORS);
	StretchBlt(hDC, 0, 0, m_wWinWidth, m_wWinHeight, m_hDcOff, 0,0,m_wWidth,m_wHeight, SRCCOPY);

	ReleaseDC(m_hWnd, hDC);
}
